import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DiscordSnowflake } from "@sapphire/snowflake";
import * as argon2 from "argon2";
import { plainToInstance } from "class-transformer";
import { DataSource, Repository } from "typeorm";
import { ResultMsg } from "@/common/enum/result-msg.enum";
import Result from "@/common/utils/result.util";
import { SysRole } from "@/database/entity/SysRole";
import { SysUser } from "@/database/entity/SysUser";
import { SysUserRole } from "@/database/entity/SysUserRole";
import { CreateUserDto } from "./dto/create-user.dto";
import { PageUserDto } from "./dto/page-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { UserPageVo } from "./vo/user-page.vo";
import { UserPasswordVo } from "./vo/user-password.vo";

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(SysUser) private readonly userRepository: Repository<SysUser>,
        @InjectRepository(SysUserRole) private readonly userRoleRepository: Repository<SysUserRole>,
        private readonly dataSource: DataSource,
    ) {}

    /** 新增用户 */
    async create(user: CreateUserDto) {
        const id = DiscordSnowflake.generate().toString();
        const password = await getDefaultPassword(user.username);
        const newUser = this.userRepository.create({ ...user, id, password });
        await this.userRepository.save(newUser);
        return Result.success(id);
    }

    /** 删除用户 */
    async remove(id: string) {
        // TODO 回收站功能, 不需要的话可以将关联表中的数据一并删除
        await this.userRepository.update(id, { deletedAt: new Date() });
        return Result.success(null, ResultMsg.HANDLE_SUCCESS);
    }

    /** 更新用户信息 */
    async update(user: UpdateUserDto) {
        await this.userRepository.update(user.id, { ...user });
        return Result.success(null, ResultMsg.UPDATE_SUCCESS);
    }

    /** 更新用户状态 */
    async updateStatus(ids: string[], status: boolean) {
        await this.userRepository.update(ids, { status });
        return Result.success(null, ResultMsg.UPDATE_SUCCESS);
    }

    /** 重置用户密码 */
    async resetPassword(ids: string[]) {
        this.dataSource.transaction(async (manager) => {
            for (const id of ids) {
                const user = await manager.findOneBy(SysUser, { id });
                if (user) {
                    const password = await getDefaultPassword(user.username);
                    await manager.update(SysUser, id, { password });
                }
            }
        });
        return Result.success(null, ResultMsg.HANDLE_SUCCESS);
    }

    /** 分配角色 */
    async assignRoles(userId: string, roleIds: string[]) {
        this.dataSource.transaction(async (manager) => {
            // 先删除已有角色
            await manager.delete(SysUserRole, { userId });
            // 分配新角色
            const userRoles = roleIds.map((roleId) => this.userRoleRepository.create({ userId, roleId }));
            await manager.save(SysUserRole, userRoles);
        });
        return Result.success(null, ResultMsg.HANDLE_SUCCESS);
    }

    /** 分页查询用户列表 */
    async page(data: PageUserDto) {
        const { deptId, username, phone, status, pageNum, pageSize } = data;

        // 先查询用户列表（不包含聚合）
        const queryBuilder = this.userRepository
            .createQueryBuilder("su")
            .where("su.deptId = :deptId", { deptId })
            .andWhere("su.deletedAt IS NULL");

        if (username) {
            queryBuilder.andWhere("su.username LIKE :username", { username: `%${username}%` });
        }
        if (phone) {
            queryBuilder.andWhere("su.phone = :phone", { phone });
        }
        if (status != null) {
            queryBuilder.andWhere("su.status = :status", { status });
        }

        queryBuilder
            .orderBy("su.createdAt", "DESC")
            .skip((pageNum - 1) * pageSize)
            .take(pageSize);

        const [list, total] = await queryBuilder.getManyAndCount();
        console.log(list, 33);

        // 获取用户ID列表
        const userIds = list.map((user) => user.id);

        // 如果有用户，查询角色信息
        let userRoleMap: Record<string, string[]> = {};
        if (userIds.length > 0) {
            const roleQuery = await this.dataSource
                .createQueryBuilder()
                .select("sur.user_id", "userId")
                .addSelect("array_agg(sr.name)", "roleNames")
                .from(SysUserRole, "sur")
                .leftJoin(SysRole, "sr", "sur.role_id = sr.id")
                .where("sur.user_id IN (:...userIds)", { userIds })
                .groupBy("sur.user_id")
                .getRawMany();

            userRoleMap = roleQuery.reduce(
                (map, item) => {
                    map[item.userId] = item.roleNames || [];
                    return map;
                },
                {} as Record<string, string[]>,
            );
        }

        // 组装数据
        const userList = list.map((user) =>
            plainToInstance(
                UserPageVo,
                {
                    ...user,
                    roleNames: userRoleMap[user.id] || [],
                },
                { excludeExtraneousValues: true, ignoreDecorators: true },
            ),
        );

        return Result.page(userList, { pageNum, pageSize, total });
    }

    /** 根据名称查找用户 */
    async findByUsername(username: string): Promise<UserPasswordVo | null> {
        // 这里使用原生 SQL 查询, TypeORM 的写法太复杂了
        const sql = `
            SELECT su.*,
                (SELECT array_agg(sr.code)
                FROM sys_user_role sur
                    JOIN sys_role sr ON sur.role_id = sr.id
                WHERE sur.user_id = su.id) AS roles
            FROM sys_user su
            WHERE su.username = $1
                AND su.deleted_at IS NULL;
        `;
        const query: any[] | undefined = await this.userRepository.query(sql, [username]);
        if (!query || query.length === 0) {
            return null;
        } else {
            const userVo = plainToInstance(UserPasswordVo, query[0], { excludeExtraneousValues: true });
            return userVo;
        }
    }
}

/** 获取默认密码 */
async function getDefaultPassword(username: string) {
    return await argon2.hash(`${username}@Aa123456`);
}
